

using System;
#if UNITY_ENV
using UnityEngine;
#endif

namespace Tool.PEMath
{
    public struct PEVector3
    {
        public PEInt x;
        public PEInt y;
        public PEInt z;

        public PEVector3(PEInt x, PEInt y, PEInt z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

#if UNITY_ENV
        public PEVector3(Vector3 v) 
        {
            this.x = (PEInt)v.x;
            this.y = (PEInt)v.y;
            this.z = (PEInt)v.z;
        }
#endif

        public PEInt this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return x;
                    case 1:
                        return y;
                    case 2:
                        return z;
                    default:
                        return 0;
                }
            }
            set
            {
                switch (index)
                {
                    case 0:
                        x = value;
                        break;
                    case 1:
                        y = value;
                        break;
                    case 2:
                        z = value;
                        break;
                }
            }
        }

        #region 定义常用向量

        public static readonly PEVector3 zero =new PEVector3(0, 0, 0);
        public static readonly PEVector3 one = new PEVector3(1, 1, 1);
        public static readonly PEVector3 forward =new PEVector3(0, 0, 1);
        public static readonly PEVector3 back = new PEVector3(0, 0, -1);
        public static readonly PEVector3 left = new PEVector3(-1, 0, 0);
        public static readonly PEVector3 right = new PEVector3(1, 0, 0);
        public static readonly PEVector3 up = new PEVector3(0, 1, 0);
        public static readonly PEVector3 down = new PEVector3(0, -1, 0);

        #endregion

        #region 运算符

        public static PEVector3 operator +(PEVector3 v1, PEVector3 v2)
        {
            PEInt x = v1.x + v2.x;
            PEInt y = v1.y + v2.y;
            PEInt z = v1.z + v2.z;
            return new PEVector3(x, y, z);
        }

        public static PEVector3 operator -(PEVector3 v1, PEVector3 v2)
        {
            PEInt x = v1.x - v2.x;
            PEInt y = v1.y - v2.y;
            PEInt z = v1.z - v2.z;
            return new PEVector3(x, y, z);
        }

        public static PEVector3 operator *(PEVector3 v, PEInt value)
        {
            PEInt x = v.x * value;
            PEInt y = v.y * value;
            PEInt z = v.z * value;
            return new PEVector3(x, y, z);
        }

        public static PEVector3 operator *(PEInt value, PEVector3 v)
        {
            PEInt x = v.x * value;
            PEInt y = v.y * value;
            PEInt z = v.z * value;
            return new PEVector3(x, y, z);
        }

        public static PEVector3 operator /(PEVector3 v, PEInt value)
        {
            PEInt x = v.x / value;
            PEInt y = v.y / value;
            PEInt z = v.z / value;
            return new PEVector3(x, y, z);
        }

        public static PEVector3 operator -(PEVector3 v)
        {
            PEInt x = -v.x;
            PEInt y = -v.y;
            PEInt z = -v.z;
            return new PEVector3(x, y, z);
        }

        public static bool operator ==(PEVector3 v1, PEVector3 v2)
        {
            return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
        }

        public static bool operator !=(PEVector3 v1, PEVector3 v2)
        {
            return v1.x != v2.x || v1.y != v2.y || v1.z != v2.z;
        }

        #endregion

        
        //当前向量长度平方
        public PEInt sqrMagnitude => x * x + y * y + z * z;

        /**
         * 返回传入参数的向量长度平方
         */
        public static PEInt SqrMagnitude(PEVector3 v) =>v.x * v.x + v.y * v.y + v.z * v.z;

        public PEInt magnitude => PECalc.Sqrt(sqrMagnitude);
        
        // 返回当前定点向量的单位向量
        public PEVector3 normalized
        {
            get
            {
                if (magnitude > 0)
                {
                    PEInt rate = PEInt.one / magnitude;
                    return new PEVector3(x * rate, y * rate, z * rate);
                }
                return zero;
            }
        }
        
        /**
         * 规格化当前向量为单位向量
         */
        public void Normalize() 
        {
            PEInt rate = PEInt.one / magnitude;
            x *= rate;
            y *= rate;
            z *= rate;
        }
        
        /**
         * 旋转向量一定的角度 仅限二维xz
         */
        public PEVector3 Rotate(int angle)
        {
            var cos = PECalc.Cos(angle);
            var sin = PECalc.Sin(angle);
            return new PEVector3(x * cos - z * sin, 0, z * cos + x * sin);
        }
        
        /**
         * 返回传入参数向量的单位向量
         */
        public static PEVector3 Normalize(PEVector3 v)
        {
            if(v.magnitude > 0) 
            {
                PEInt rate = PEInt.one / v.magnitude;
                return new PEVector3(v.x * rate, v.y * rate, v.z * rate);
            }
            return zero;
        }
        
        /**
         * 点乘
         */
        public static PEInt Dot(PEVector3 a, PEVector3 b) 
        {
            return a.x * b.x + a.y * b.y + a.z * b.z;
        }
        
        /**
         * 叉乘
         */
        public static PEVector3 Cross(PEVector3 a, PEVector3 b) 
        {
            return new PEVector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
        }
        
        /**
         * 获取向量夹角：向量夹角（弧度值）=arcos(a向量*b向量/|a|*|b|)
         */
        public static PEArgs Angle(PEVector3 from, PEVector3 to) 
        {
            PEInt dot = Dot(from, to);
            PEInt mod = from.magnitude * to.magnitude;
            if(mod == 0) 
                return PEArgs.Zero;
            PEInt value = dot / mod; //-1~1
            return PECalc.Acos(value);
        }
        
        /**
         * 获取向量夹角：向量夹角（弧度值）=arcos(a向量*b向量/|a|*|b|)
         */
        public static PEInt RadianValue(PEVector3 from, PEVector3 to) 
        {
            PEInt dot = Dot(from, to);
            PEInt mod = from.magnitude * to.magnitude;
            if(mod == 0) 
                return PEInt.zero;
            PEInt value = dot / mod; //-1~1
            return PECalc.Acos2RadianValue(value);
        }

#if UNITY_ENV
        /**
         * 获取浮点数向量（注意：不可再进行逻辑运算，用于表现层只读） 
         */
        public Vector3 ConvertViewVector3() => new Vector3(x.RawFloat, y.RawFloat, z.RawFloat);
#endif
      
        /**
         * 获取放大后向量xyz值的的数组
         */
        public long[] ConvertLongArray() => new[] {x.ScaledValue, y.ScaledValue, z.ScaledValue};

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            PEVector3 v = (PEVector3) obj;
            return v.x == x && v.y == y && v.z == z;
        }

        public override int GetHashCode() => x.GetHashCode();

        public override string ToString() => $"x:{x} y:{y} z:{z}";
    }
}